package hibernatemock.persistence;

import hibernatemock.ReflectionUtil;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

public class RecursiveSearch {

	private PropertyCallBack propertyCallBack;
	private ReflectionUtil reflectionUtil = new ReflectionUtil();
	private List<Object> avoidCyclic = new ArrayList<Object>();

	public RecursiveSearch(PropertyCallBack propertyCallBack) {
		this.propertyCallBack = propertyCallBack;
	}

	public void start(Object rootEntity) {
		startReal(rootEntity);
		avoidCyclic.clear();
	}

	private void startReal(Object rootEntity) {
		if (rootEntity != null) {
			PropertyDescription propertyDescription = new PropertyDescription();
			propertyDescription.setRootObject(rootEntity);
			if (rootEntity.getClass().isAnnotationPresent(Entity.class)) {
				propertyDescription.setIsRootObjectEntity(true);
				for (Field field : rootEntity.getClass().getDeclaredFields()) {
					propertyDescription = new PropertyDescription();
					propertyDescription.setRootObject(rootEntity);
					propertyDescription.setIsRootObjectEntity(true);
					if (!avoidCyclic.contains(field)) {
						avoidCyclic.add(field);
						field.setAccessible(true);
						propertyDescription.setField(field);
						if (reflectionUtil.isAnnotationPresent(field, Id.class)) {
							propertyDescription.setIsId(true);
							propertyDescription.setId(reflectionUtil.getProperty(rootEntity, field));
						}
						propertyDescription.setFieldObject(reflectionUtil.getProperty(rootEntity, field));
						if (reflectionUtil.isAnnotationPresent(field, GeneratedValue.class))
							propertyDescription.setIsAutoIncrementId(true);
						if (Collection.class.isAssignableFrom(field.getType())) {
							propertyDescription.setIsCollection(true);
							Collection<?> collection = (Collection<?>) reflectionUtil.getProperty(rootEntity, field);
							if (collection != null) {
								for (Object object : collection) {
									propertyCallBack.propertyCallBack(propertyDescription);
									startReal(object);
								}
							}
						} else {
							propertyCallBack.propertyCallBack(propertyDescription);
							startReal(reflectionUtil.getProperty(rootEntity, field));
						}
					}
				}
			}
		}
	}

}
